package com.weilele.mvvm.utils.android_r

import android.animation.ValueAnimator
import android.view.View
import android.view.Window
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.*
import androidx.core.view.animation.PathInterpolatorCompat
import androidx.fragment.app.Fragment
import com.weilele.mvvm.base.helper.ILifecycleObserver
import com.weilele.mvvm.base.livedata.appCompatActivity

/**
 * 监听键盘高度变化，兼容安卓11以下机型
 * 安卓11以下，不支持控制键盘移动
 * 对话框使用，需要传入activity的window对象，如果传入dialog的window对象，会导致监听无效
 */
class ImeHelper2(
    window: Window,
    private val needWindowInsetsListener: Boolean = false/*如果键盘事件不回调，可以尝试把这里设置为true*/
) : ILifecycleObserver {
    companion object {
        operator fun invoke(
            fragment: Fragment,
            needWindowInsetsListener: Boolean = false,/*是否监听窗口变化*/
            addObserver: Boolean = true,/*添加生命周期*/
        ): ImeHelper2 {
            return ImeHelper2(fragment.appCompatActivity!!, needWindowInsetsListener).apply {
                if (addObserver) {
                    fragment.lifecycle.addObserver(this)
                }
            }
        }

        operator fun invoke(
            activity: AppCompatActivity,
            needWindowInsetsListener: Boolean = false,/*是否监听窗口变化*/
            addObserver: Boolean = true,/*添加生命周期*/
        ): ImeHelper2 {
            return ImeHelper2(activity.window, needWindowInsetsListener).apply {
                if (addObserver) {
                    activity.lifecycle.addObserver(this)
                }
            }
        }
    }

    private val decorView = window.decorView

    init {
        initIme()
    }

    /**
     * 每次动画，键盘变化的总高度，如果有导航栏则包含导航栏高度
     */
    private var animTotalImeOffSet = 0
    private var _statusBarHeight: Int = 0
    private var _navigationBarHeight: Int = 0


    private fun initIme() {
        var canCallProgress = false
        ViewCompat.setWindowInsetsAnimationCallback(decorView,
            object :
                WindowInsetsAnimationCompat.Callback(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
                override fun onProgress(
                    insets: WindowInsetsCompat,
                    runningAnimations: MutableList<WindowInsetsAnimationCompat>
                ): WindowInsetsCompat {
                    listenerInfo(insets, true)
                    return insets
                }

                override fun onPrepare(animation: WindowInsetsAnimationCompat) {
                    //如果这里回调可以走，那么onProgress回调就会走
                    canCallProgress = true
                    super.onPrepare(animation)
                }

                override fun onEnd(animation: WindowInsetsAnimationCompat) {
                    super.onEnd(animation)
                    canCallProgress = false
                }

                override fun onStart(
                    animation: WindowInsetsAnimationCompat,
                    bounds: WindowInsetsAnimationCompat.BoundsCompat
                ): WindowInsetsAnimationCompat.BoundsCompat {
                    //因为键盘是屏幕最下面弹出来，最终是在导航栏上方
                    //所以这里的[insets.getInsets(WindowInsetsCompat.Type.ime())]获取高度，如果有导航栏，则是导航栏+真实键盘的高度
                    //如果导航栏不存在，[insets.getInsets(WindowInsetsCompat.Type.ime())]获取的就是键盘真实高度
                    animTotalImeOffSet = bounds.upperBound.bottom
                    return super.onStart(animation, bounds)
                }
            })
        if (needWindowInsetsListener) {
            ViewCompat.setOnApplyWindowInsetsListener(decorView) { v, insets ->
                if (!canCallProgress) {//当无法回调键盘动画的时候可以使用这个回调
                    listenerInfo(insets, false)
                }
                insets
            }
        }
    }

    /**
     * 监听器
     */
    private var listener: ((offSetImeHeight: Int, imeHeight: Int, statusBarHeight: Int, navigationBarHeight: Int) -> Unit)? =
        null


    private fun listenerInfo(insets: WindowInsetsCompat, isProgress: Boolean) {
        //状态栏高度
        _statusBarHeight = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top
        //导航栏高度
        _navigationBarHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
        //键盘高度
        val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
        if (!isProgress && imeHeight > 0) {
            animTotalImeOffSet = imeHeight
        }
        if (isProgress) {
            listener?.invoke(imeHeight, animTotalImeOffSet, statusBarHeight, navigationBarHeight)
        } else {
            animChange(imeHeight)
        }
    }


    private var lastOffSet = 0

    private val maxAnimatorDuration = 185L

    private val anim by lazy {
        ValueAnimator.ofInt().apply {
            duration = maxAnimatorDuration
            interpolator = PathInterpolatorCompat.create(1f, 1f)
            addUpdateListener {
                val value = it.animatedValue as Int
                listener?.invoke(value, animTotalImeOffSet, statusBarHeight, navigationBarHeight)
            }
        }
    }

    private fun animChange(offset: Int) {
        if (offset == lastOffSet) {
            return
        }
        anim.cancel()
        anim.setIntValues(lastOffSet, offset)
        lastOffSet = offset
        anim.start()
    }

    val statusBarHeight: Int
        get() = _statusBarHeight

    val navigationBarHeight: Int
        get() = _navigationBarHeight

    /**
     * 不包含导航栏的键盘高度
     */
    val imeHeight: Int
        get() = animTotalImeOffSet - navigationBarHeight

    /**
     * 设置监听
     */
    fun setOnImeListener(
        listener: ((
            imeOffset: Int,
            imeMaxHeight: Int/*包含导航栏和状态栏总高度*/,
            statusBarHeight: Int,
            navigationBarHeight: Int
        ) -> Unit)?
    ) {
        this.listener = listener
    }

    override fun onDestroy() {
        super.onDestroy()
        this.listener = null
        if (anim.isRunning) {
            anim.cancel()
        }
        anim.removeAllUpdateListeners()
        if (needWindowInsetsListener) {
            ViewCompat.setOnApplyWindowInsetsListener(decorView, null)
        }
        ViewCompat.setWindowInsetsAnimationCallback(decorView, null)
    }
}

/**
 * 显示键盘.
 * 如果view是editText，可以显示键盘，并且这个editText获取焦点
 * 其他view，如果当前有其他editText有焦点，则能弹出，当前的焦点view不会发生变化，如果没有焦点view，怎不会弹出键盘
 */
fun View.showIme() {
    isFocusableInTouchMode = true
    requestFocus()
    ViewCompat.getWindowInsetsController(this)?.show(WindowInsetsCompat.Type.ime())
}

/**
 * 隐藏键盘，
 * 任意view均可隐藏
 */
fun View.hideIme() {
    ViewCompat.getWindowInsetsController(this)?.hide(WindowInsetsCompat.Type.ime())
}